package cn.zwk.handler.impl;

import cn.zwk.handler.Handler;
import cn.zwk.handler.model.ShResult;
import cn.zwk.template.FreemarkerRenderer;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
public class ShHandler implements Handler {

    @Override
    public Object beforeHandle(Map<String, Object> envs, String key, String query, String[] row) {
        Objects.requireNonNull(envs, "envs");
        Map<String, Object> model = baseContext(envs, key, query, row);
        String template = str(envs.getOrDefault("cmdTemplate", envs.getOrDefault("cmd", "echo ${key} ${query}")));
        String command = FreemarkerRenderer.renderString(template, model);

        String timeoutTmpl = str(envs.getOrDefault("timeoutMs", "15000"));
        long timeoutMs = longVal(FreemarkerRenderer.renderString(timeoutTmpl, model));
        boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
        String shell = str(envs.get("shell"));
        if (shell != null) shell = FreemarkerRenderer.renderString(shell, model);

        ProcessBuilder pb;
        if (shell != null && !shell.isBlank()) {
            pb = new ProcessBuilder(shell, command);
        } else if (isWindows) {
            pb = new ProcessBuilder("cmd", "/c", command);
        } else {
            pb = new ProcessBuilder("bash", "-lc", command);
        }

        // 继承环境变量
        Map<String, String> pbEnv = pb.environment();
        for (Map.Entry<String, Object> e : envs.entrySet()) {
            if (e.getValue() instanceof String s) {
                pbEnv.put(e.getKey(), FreemarkerRenderer.renderString(s, model));
            }
        }

        pb.redirectErrorStream(false);
        try {
            Process p = pb.start();
            boolean finished = p.waitFor(timeoutMs, TimeUnit.MILLISECONDS);
            if (!finished) {
                p.destroyForcibly();
                return new ShResult(124, "", "Timeout after " + timeoutMs + " ms");
            }
            String stdout = readAll(p.getInputStream().readAllBytes());
            String stderr = readAll(p.getErrorStream().readAllBytes());
            return new ShResult(p.exitValue(), stdout, stderr);
        } catch (IOException | InterruptedException ex) {
            Thread.currentThread().interrupt();
            return new ShResult(125, "", ex.toString());
        }
    }

    @Override
    public boolean handleStatus(Map<String, Object> envs, String key, String query, String[] row, Object res) {
        ShResult r = (res instanceof ShResult sr) ? sr : new ShResult(-1, String.valueOf(res), "");
        Map<String, Object> model = baseContext(envs, key, query, row);
        model.put("code", r.getCode());
        model.put("stdout", r.getStdout());
        model.put("stderr", r.getStderr());
        String statusTpl = str(envs.get("statusTemplate"));
        if (statusTpl == null || statusTpl.isBlank()) {
            statusTpl = "<#if code==0>1<#else>0</#if>"; // 默认：退出码0为成功
        }
        try {
            String rendered = FreemarkerRenderer.renderString(statusTpl, model);
            String norm = rendered == null ? "" : rendered.trim();
            return "1".equals(norm);
        } catch (RuntimeException e) {
            return r.getCode() == 0;
        }
    }

    @Override
    public String handleSnippet(Map<String, Object> envs, String key, String query, String[] row, Object res) {
        ShResult r = (res instanceof ShResult sr) ? sr : new ShResult(-1, String.valueOf(res), "");
        String tmpl = str(envs.getOrDefault("snippetTemplate", "code=${code} stdout=${(stdout!\"\")?truncate(120)}"));
        Map<String, Object> model = baseContext(envs, key, query, row);
        model.put("code", r.getCode());
        model.put("stdout", r.getStdout());
        model.put("stderr", r.getStderr());
        try {
            return FreemarkerRenderer.renderString(tmpl, model);
        } catch (RuntimeException e) {
            String outSafe = r.getStdout() == null ? "" : (r.getStdout().length() > 200 ? r.getStdout().substring(0, 200) : r.getStdout());
            return "code=" + r.getCode() + " stdout=" + outSafe;
        }
    }

    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static long longVal(Object o) {
        if (o == null) return 0L;
        if (o instanceof Number n) return n.longValue();
        try { return Long.parseLong(String.valueOf(o)); } catch (Exception e) { return 0L; }
    }
    private static String readAll(byte[] bytes) { return new String(bytes, StandardCharsets.UTF_8); }

    private static Map<String, Object> baseContext(Map<String, Object> envs, String key, String query, String[] row) {
        Map<String, Object> ctx = new HashMap<>();
        ctx.put("envs", envs);
        ctx.put("key", key);
        ctx.put("query", query);
        ctx.put("row", row == null ? List.of() : java.util.Arrays.asList(row));
        return ctx;
    }

    // no custom placeholder renderer; using Freemarker for all templates
}
